#include "plock.hpp"

/**
 * @brief Construct a new plock::plock object, open sem file, if file name equal null string "", lock is bypassed
 * 
 * @param name name of the sem file, will be created if not exists
 */
plock::plock(std::string name)
{
	if (name == "")
	{
		this->lock_cnt = -1;
		return;
	}
	this->name = name;
	this->sem = sem_open(name.c_str(), O_CREAT, 0666, 1);
	this->lock_cnt = 0;
}

/**
 * @brief Destroy the plock::plock object, close sem file for this process, the file still exists
 * 
 */
plock::~plock()
{
	if (this->lock_cnt < 0)
		return;
	if (this->lock_cnt > 0)
	{
		sem_post(this->sem);
		this->lock_cnt = 0;
	}
	sem_close(this->sem);
	sem_unlink(this->name.c_str());
}

/**
 * @brief try lock, nonblocking, recursive
 * 
 * @return int 0: successful, <0: failed
 */
int plock::trylock()
{
	if (this->lock_cnt < 0)
		return 0;
	if (this->lock_cnt > 0)
	{
		this->lock_cnt += 1;
		return 0;
	}
	int ret = sem_trywait(this->sem);
	if (ret == 0)
	{
		this->lock_cnt = 1;
		return 0;
	}
	return ret;
}

/**
 * @brief try get lock with timeout, recursive
 * 
 * @param us timeout
 * @return int 0: successful, <0: failed, timeout
 */
int plock::trylock_for(int us)
{
	if (this->lock_cnt < 0)
		return 0;
	if (this->lock_cnt > 0)
	{
		this->lock_cnt += 1;
		return 0;
	}
	struct timespec endtime;
	timeval starttime;
	gettimeofday(&starttime, NULL);
	endtime.tv_sec = starttime.tv_sec + us / 1000000;
	us %= 1000000;
	endtime.tv_nsec = starttime.tv_usec * 1000 + us * 1000;
	endtime.tv_sec += endtime.tv_nsec / 1000000000;
	endtime.tv_nsec %= 1000000000;
	int ret = sem_timedwait(this->sem, &endtime);
	if (ret == 0)
	{
		this->lock_cnt = 1;
		return 0;
	}
	return ret;
}

/**
 * @brief release the lock, recursive
 * 
 * @return int 0: successful, <0: failed
 */
int plock::unlock()
{
	if (this->lock_cnt <= 0)
		return 0;
	this->lock_cnt -= 1;
	if (this->lock_cnt > 0)
	{
		return 0;
	}
	return sem_post(this->sem);
}


